package org.zjvis.datascience.service;

import cn.hutool.core.util.ObjectUtil;
import com.mayabot.nlp.blas.Vector;
import com.mayabot.nlp.fasttext.FastText;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.zjvis.datascience.common.util.ToolUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

/**
 * @description FastText 模型 预测服务Service
 * @date 2021-12-23
 */
@Service
public class FastTextService {

    private final static Logger logger = LoggerFactory.getLogger(FastTextService.class);

    private FastText autojoinModel;

    @Autowired
    private MinioService minioService;

    public FastText getAutojoinModel() {
        if (ObjectUtil.isNull(autojoinModel)) {
            try {
                autojoinModel = loadModel("autojoin");
//			mappingModel = loadModel("mapping");
            } catch (Exception e) {
                logger.error("load fasttext model error...", e);
            }
        }
        return this.autojoinModel;
    }

//	public FastText getMappingModel() {
//		return this.mappingModel;
//	}

    public void convertModel(String modelPath, String savePath, boolean compress) throws Exception {
        FastText autojoinModel = FastText.loadCppModel(new File(modelPath));
        if (compress) {
            autojoinModel = autojoinModel.quantize(2, false, false);
        }
        autojoinModel.saveModelToSingleFile(new File(savePath));
    }

    private FastText loadModel(String fileName) throws Exception {
        String modelPath = System.getProperty("user.dir") + "/" + fileName;
        File modelFile = new File(modelPath);
        if (!modelFile.exists()) {
            logger.info("Fasttext model " + fileName + " not exist, downloading...");
            InputStream mappingStream = minioService.getObject("fasttext-model", fileName);
            this.writeToLocal(modelPath, mappingStream);
        }
        FastText model = FastText.loadModelFromSingleFile(new File(modelPath));
        logger.info("Loaded fasttext model " + fileName);
        return model;
    }

    /**
     * 将InputStream写入本地文件
     *
     * @param destination 写入本地目录
     * @param input       输入流
     * @throws IOException
     */
    private void writeToLocal(String destination, InputStream input)
            throws IOException {
        int index;
        byte[] bytes = new byte[1024];
        FileOutputStream downloadFile = new FileOutputStream(destination);
        while ((index = input.read(bytes)) != -1) {
            downloadFile.write(bytes, 0, index);
            downloadFile.flush();
        }
        downloadFile.close();
        input.close();
    }

    public double getFasttext(FastText model, String str1, String str2) {
        str1 = ToolUtil.standardString(str1, ' ');
        double[] v1 = getTextVector(model, str1);
        str2 = ToolUtil.standardString(str2, ' ');
        double[] v2 = getTextVector(model, str2);
        double ret = computeVectorSimilarity(v1, v2);
        return ret;
    }

    public double[] getTextVector(FastText model, String text) {
        Vector rawRes = model.getSentenceVector(Arrays.asList(text.split(" ")));
        double[] res = vectorToArray(rawRes);
        return res;
    }

    public double[] vectorToArray(Vector v) {
        double[] res = new double[v.length()];
        for (int i = 0; i < v.length(); i++) {
            res[i] = v.get(i);
        }
        return res;
    }

    private double normArray(double[] v) {
        double res = 0d;
        for (int i = 0; i < v.length; i++) {
            res += Math.pow(v[i], 2);
        }
        return Math.sqrt(res);
    }

    private double mulArray(double[] v1, double[] v2) {
        double res = 0d;
        for (int i = 0; i < v1.length; i++) {
            res += v1[i] * v2[i];
        }
        return res;
    }

    public double computeVectorSimilarity(double[] v1, double[] v2) {
        //计算两个向量之间的余弦相似度
        double num = mulArray(v1, v2);
        double den = normArray(v1) * normArray(v2);
        double res = 0d;
        if (den != 0) {
            res = num / den;
            res = res > 0 ? res : 0;
        }
        return res;
    }

}
